
package com.gitee.jmash.rbac.service;

import com.gitee.jmash.common.excel.ExcelImport;
import com.gitee.jmash.common.lock.DistributedLock;
import com.gitee.jmash.common.utils.UUIDUtil;
import com.gitee.jmash.core.jaxrs.ParamsValidationException;
import com.gitee.jmash.core.orm.cdi.JpaTenantService;
import com.gitee.jmash.core.transaction.JakartaTransaction;
import com.gitee.jmash.core.utils.FieldMaskUtil;
import com.gitee.jmash.file.client.cdi.FileClient;
import com.gitee.jmash.rbac.entity.DeptEntity;
import com.gitee.jmash.rbac.excel.DeptHeaderImport;
import com.gitee.jmash.rbac.mapper.DeptMapper;
import jakarta.enterprise.inject.Typed;
import jakarta.inject.Inject;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.transaction.Transactional;
import jakarta.transaction.Transactional.TxType;
import jakarta.validation.ValidationException;
import jakarta.validation.executable.ValidateOnExecution;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import jmash.rbac.protobuf.DeptCountUpdateReq;
import jmash.rbac.protobuf.DeptCreateReq;
import jmash.rbac.protobuf.DeptEnableKey;
import jmash.rbac.protobuf.DeptImportReq;
import jmash.rbac.protobuf.DeptMoveKey;
import jmash.rbac.protobuf.DeptUpdateReq;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 组织机构 rbac_dept写服务.
 *
 * @author <a href="mailto:service@crenjoy.com">crenjoy</a>
 */
@Typed(DeptWrite.class)
@Transactional(TxType.REQUIRED)
@JpaTenantService
@ValidateOnExecution
public class DeptWriteBean extends DeptReadBean implements DeptWrite, JakartaTransaction {

  private static Log log = LogFactory.getLog(DeptWriteBean.class);
  @Inject
  DistributedLock lock;

  @Override
  @PersistenceContext(unitName = "WriteRbac")
  public void setEntityManager(EntityManager entityManager) {
    this.tem.setEntityManager(entityManager, true);
  }

  @Override
  public DeptEntity insert(DeptCreateReq dept) {
    DeptEntity entity = DeptMapper.INSTANCE.create(dept);
    // 1.业务校验.
    if (deptDao.deptCodeExist(null, dept.getDeptCode())) {
      throw new ValidationException("部门编码已存在");
    }
    // 2.仅校验,不执行.
    if (dept.getValidateOnly()) {
      return entity;
    }
    // 3.检查是否重复请求.
    if (!lock.lock(dept.getRequestId(), 60)) {
      throw new ParamsValidationException("requestId", "客户端发起重复请求");
    }
    // 设置ParentId,深度和排序
    deptDao.insertTree(entity, UUIDUtil.fromString(dept.getParentId()), Collections.emptyMap());
    // 设置开放访问状态
    if (!UUIDUtil.emptyUUID().equals(entity.getParentId())) {
      DeptEntity parent = deptDao.find(entity.getParentId());
      entity.setIsOpen(parent.getIsOpen());
    }
    // 4.执行业务(创建人及时间内部处理.)
    deptDao.persist(entity);
    return entity;
  }

  @Override
  public DeptEntity update(DeptUpdateReq req) {
    // 1.业务校验.
    if (deptDao.deptCodeExist(UUIDUtil.fromString(req.getDeptId()), req.getDeptCode())) {
      throw new ValidationException("部门编码已存在!");
    } ;
    DeptEntity entity = deptDao.find(UUIDUtil.fromString(req.getDeptId()), req.getValidateOnly());
    if (null == entity) {
      throw new ValidationException("找不到实体:" + req.getDeptId());
    }
    // 无需更新,返回当前数据库数据.
    if (req.getUpdateMask().getPathsCount() == 0) {
      return entity;
    }
    // 放到FieldMaskUtil.copyMask前
    UUID newParentId = UUIDUtil.fromString(req.getParentId());
    UUID oldParentId = entity.getParentId();
    // 更新掩码属性
    FieldMaskUtil.copyMask(entity, req, req.getUpdateMask());
    // 1.业务校验.
    // 2.仅校验,不执行.
    if (req.getValidateOnly()) {
      deptDao.detach(entity);
      return entity;
    }
    // 3.检查是否重复请求.
    if (!lock.lock(req.getRequestId(), 60)) {
      throw new ParamsValidationException("requestId", "客户端发起重复请求");
    }
    // 更新ParentId,深度和排序
    deptDao.updateTree(entity, oldParentId, newParentId, Collections.emptyMap());
    // 更新开放访问状态
    adjustOpenByParent(entity.getDeptId(), entity.getIsOpen());
    // 4.执行业务
    deptDao.merge(entity);
    return entity;
  }

  /** 调整开放状态. */
  protected void adjustOpenByParent(UUID parentId, Boolean open) {
    List<DeptEntity> list = deptDao.findByParent(parentId, Collections.emptyMap());
    for (DeptEntity dept : list) {
      dept.setIsOpen(open);
      deptDao.merge(dept);
      adjustOpenByParent(dept.getDeptId(), open);
    }
  }

  @Override
  public DeptEntity delete(UUID entityId) {
    boolean isRelation = hasRelationDept(entityId);
    if (isRelation) {
      throw new UnsupportedOperationException("部门存在关联数据，无法删除！");
    }
    DeptEntity entity = deptDao.removeById(entityId);
    return entity;
  }

  @Override
  public Integer batchDelete(Set<UUID> entityIds) {
    int i = 0;
    for (UUID entityId : entityIds) {
      boolean isRelation = hasRelationDept(entityId);
      if (!isRelation) {
        deptDao.removeById(entityId);
        i++;
      }
    }
    return i;
  }

  /**
   * 判断部门是否存在关联.
   */
  private boolean hasRelationDept(UUID entityId) {
    // 判断是否存在下级
    Long count = deptDao.findCountByParent(entityId, Collections.emptyMap());
    if (count > 0) {
      return true;
    }
    // 判断部门是否存在用户关联
    int userCount = usersJobsDao.hasExistUserByDeptID(entityId);
    if (userCount > 0) {
      return true;
    }
    return false;
  }

  @Override
  public boolean moveOrderBy(DeptMoveKey deptMoveKey) {
    UUID entityId = UUIDUtil.fromString(deptMoveKey.getDeptId());
    DeptEntity entity = findById(entityId);
    if (null == entity) {
      throw new ValidationException("找不到实体:" + entityId.toString());
    }
    return deptDao.moveOrderBy(deptMoveKey.getUp(), entity, Collections.emptyMap());
  }

  @Override
  public Integer lock(Set<UUID> entityIds, boolean enable) {
    int i = 0;
    for (UUID entityId : entityIds) {
      DeptEntity entity = findById(entityId);
      entity.setStatus(enable);
      deptDao.merge(entity);
      i++;
    }
    return i;
  }

  @Override
  public boolean enableDept(DeptEnableKey deptEnableKey) {
    UUID entityId = UUIDUtil.fromString(deptEnableKey.getDeptId());
    DeptEntity entity = findById(entityId);
    if (null == entity) {
      throw new ValidationException("找不到实体:" + entityId.toString());
    }
    if (!deptEnableKey.getEnable()) {
      boolean haveRelation = hasRelationDept(entityId);
      if (haveRelation) {
        throw new UnsupportedOperationException("部门存在关联数据，无法禁用！");
      }
    }
    entity.setStatus(deptEnableKey.getEnable());
    deptDao.merge(entity);
    return true;
  }

  @Override
  public String importDept(DeptImportReq req) throws Exception {
    // 1.检查是否重复请求.
    if (!lock.lock(req.getRequestId(), 60)) {
      throw new ParamsValidationException("requestId", "客户端发起重复请求");
    }
    // 文件检查
    File file = FileClient.downloadFile(req.getFileNames());
    if (!file.exists()) {
      log.error("找不到文件：" + req.getFileNames());
      return "找不到文件：" + req.getFileNames();
    }

    int success = 0;
    int fail = 0;

    ExcelImport excel = new ExcelImport();
    excel.addHeaders(DeptHeaderImport.getHeaderImports());
    excel.openExcel(file);
    int sheetIndex = 0;

    if (req.getAddFlag()) {
      // 新增
      List<DeptCreateReq> createDetails =
          excel.importEntity(sheetIndex, DeptCreateReq.getDefaultInstance());
      int i = 0;
      for (DeptCreateReq createReq : createDetails) {
        try {
          insert(createReq.toBuilder().setRequestId(req.getRequestId() + (i++)).build());
          success++;
        } catch (Exception ex) {
          log.error("", ex);
          fail++;
        }
      }
    } else {
      // 更新
      List<DeptUpdateReq> updateDetails =
          excel.importEntity(sheetIndex, DeptUpdateReq.getDefaultInstance());
      int i = 0;
      for (DeptUpdateReq updateReq : updateDetails) {
        try {
          update(updateReq.toBuilder().setRequestId(req.getRequestId() + (i++))
              .setUpdateMask(req.getUpdateMask()).build());
          success++;
        } catch (Exception ex) {
          log.error("", ex);
          fail++;
        }
      }
    }

    return String.format("导入成功%d行,失败%d行.<br/>%s", success, fail, excel.getErrorMsg());
  }

  @Override
  public boolean updateDeptCount(DeptCountUpdateReq req) {
    UUID entityId = UUIDUtil.fromString(req.getDeptId());
    DeptEntity entity = deptDao.find(entityId, false);
    if (null == entity) {
      throw new ValidationException("找不到实体:" + entityId.toString());
    }
    if (!lock.lock(req.getRequestId(), 60)) {
      throw new ParamsValidationException("requestId", "客户端发起重复请求");
    }
    entity.setCount(req.getCount());
    deptDao.merge(entity);
    return true;
  }
}
